package com.fand.template.support;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.UUID;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

/**
 * FreeMarkerTemplate支持
 * 
 * @author haitao.yao
 * 
 */
public class FreeMarkerTemplateSupport implements TemplateSupport {

	/**
	 * 日志
	 */
	private final static Log log = LogFactory.getLog(FreeMarkerTemplateSupport.class);

	/**
	 * freemarker.template.Configuratio
	 */
	Configuration configuration;

	/**
	 * 文件名解析格式
	 */
	private String filePathFormate = TemplateSupport.DEFAULT_FILE_PATH_FORMATE;

	/**
	 * 文件名变量名称
	 */
	private String filePathVariableName = TemplateSupport.DEFAULT_FILE_PATH_VARIABLE_NAME;

	private Boolean showModel;

	private Boolean showProcessTemplateIntoString;

	private Boolean showTemplateString;

	@Override
	@Cacheable(value = "templateCache")
	public String processString(final String templateString, final Object model) {
		StringWriter writer = null;

		Reader reader = null;

		try {
			writer = new StringWriter();

			final String name = UUID.randomUUID().toString();

			reader = new StringReader(templateString);

			final Template template = new Template(name, reader, this.configuration);

			template.process(model, writer);

			return writer.toString();
		} catch (final Exception e) {
			throw new RuntimeException(e);
		} finally {
			IOUtils.closeQuietly(reader);

			IOUtils.closeQuietly(writer);
		}
	}

	@Override
	@Cacheable(value = "templateCache")
	public String processTemplateIntoString(final String filePath, final boolean isFormatFilePath, final Object model) {
		Validate.notEmpty(filePath);

		String newFilePath = null;

		if (isFormatFilePath) {
			newFilePath = StringUtils.replace(this.filePathFormate, this.filePathVariableName, filePath);
		} else {
			newFilePath = filePath;
		}

		Template template;

		try {
			if (BooleanUtils.isTrue(this.showModel)) {
				FreeMarkerTemplateSupport.log.info(model);
			}

			template = this.configuration.getTemplate(newFilePath);

			if (BooleanUtils.isTrue(this.showTemplateString)) {
				FreeMarkerTemplateSupport.log.info(template.toString());
			}

			final String str = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);

			if (BooleanUtils.isTrue(this.showProcessTemplateIntoString)) {
				FreeMarkerTemplateSupport.log.info(str);
			}

			return str;
		} catch (final IOException e) {
			throw new IllegalArgumentException(e);
		} catch (final TemplateException e) {
			throw new IllegalArgumentException(e);
		}
	}

	@Override
	@Cacheable(value = "templateCache")
	public String processTemplateIntoString(final String filePath, final Object model) {
		return this.processTemplateIntoString(filePath, true, model);
	}

	/**
	 * 设置
	 * 
	 * @param configuration
	 *            freemarker.template.Configuratio
	 */
	public void setConfiguration(final Configuration configuration) {
		this.configuration = configuration;
	}

	/**
	 * 设置
	 * 
	 * @param filePathFormate
	 *            文件名解析格式
	 */
	public void setFilePathFormate(final String filePathFormate) {
		this.filePathFormate = StringUtils.defaultIfBlank(filePathFormate, this.filePathFormate);
	}

	/**
	 * 设置
	 * 
	 * @param filePathVariableName
	 *            文件名变量名称
	 */
	public void setFilePathVariableName(final String filePathVariableName) {
		this.filePathVariableName = StringUtils.defaultIfBlank(filePathVariableName, this.filePathVariableName);
	}

	public void setShowModel(final Boolean showModel) {
		this.showModel = showModel;
	}

	public void setShowProcessTemplateIntoString(final Boolean showProcessTemplateIntoString) {
		this.showProcessTemplateIntoString = showProcessTemplateIntoString;
	}

	public void setShowTemplateString(final Boolean showTemplateString) {
		this.showTemplateString = showTemplateString;
	}
}
